from expression import expression, environment
from jsobject import jsobject, closure
from customExceptions import *

class fieldLookup(expression):
	def __init__(self, obj, name):
		self.obj = obj
		self.name = name
	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		if(isinstance(obj, jsobject)):
			return obj.access(self.name.__str__())
		else:
			raise IncorrectArgumentExcpt("fieldLookup",type(jsobject), type(obj))

class fieldMutation(expression):
	def __init__(self, obj, name, value):
		self.obj = obj
		self.name = name
		self.value = value
	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		if(isinstance(obj, jsobject)):
			return obj.mutate(self.name.__str__(), self.value.evaluate(env))
		else:
			raise IncorrectArgumentExcpt("fieldMutation",type(jsobject), type(obj))
			
# applies closures
class methodCall(expression):
	def __init__(self, obj, name, args):
		self.obj = obj
		self.name = name
		self.args = args
	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		if(isinstance(obj, jsobject)):
			c =  obj.access(self.name.__str__()) #hopefully, a closure
			if(isinstance(c, closure)):
				params = c.getParams()
				#append arguments, with correct bindings, to new enviroment
				#call function body

				if(len(self.args) != len(params)):
					raise GeneralException("Methos call to Function " + str(self.name) + " Has " + str(len(self.args)) + " args expected "+ str(len(params)))
				else:
					newEnv = c.getEnv()
					for arg, param in zip(self.args, params):
						# I think this line should do this:
						# for each parameter in the function, add a variable('x') to the envrionment
						# 'x' has the value of the associated argument, passed in the application
						# In addition, the new environment ('newEnv') is updated as params are added
						# but the old env ('env') is not updated - not that all the argument evaluations
						# use 'env'. I think this is the way to do it.
						newEnv = environment(param, arg.evaluate(env), newEnv)
					
					try:
						newEnv.getVarRef("this")
					except VarNotFoundExcpt:
						newEnv = environment("this", obj, newEnv)
					
							
					try:
						c.evaluate(newEnv)
						return void()
					except ReturnExcpt, ret:
						return ret.getValue()

			else:
				raise IncorrectArgumentExcpt("methodCall.closure",type(closure), type(c))
		else:
			raise IncorrectArgumentExcpt("methodCall.object",type(jsobject), type(obj))
